package com.circle.interests.biz.config;

import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
import org.springframework.format.FormatterRegistry;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.io.IOException;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;

/**
 * @author zhou
 * @date 2021/4/15
 */
@Slf4j
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    /**
     * 表单日期参数格式化
     */
    @Override
    public void addFormatters(FormatterRegistry registry) {
        Converter<String, Date> dateConverter = new Converter<String, Date>() {
            @Override
            public Date convert(String source) {
                if (StringUtils.isEmpty(source)) {
                    return null;
                }
                source = source.trim();
                try {
                    if (NumberUtil.isLong(source)) {
                        //时间戳
                        Long lDate = new Long(source);
                        return new Date(lDate);
                    } else {
                        return DateUtil.parse(source);
                    }
                } catch (Exception e) {
                    throw new RuntimeException(String.format("parser %s to Date fail", source));
                }
            }
        };
        Converter<String, LocalDateTime> localDateTimeConverter = new Converter<String, LocalDateTime>() {
            @Override
            public LocalDateTime convert(String source) {
                if (!StringUtils.hasText(source)) {
                    return null;
                }
                if (NumberUtil.isNumber(source)) {
                    long timestamp = Long.parseLong(source);
                    return LocalDateTime.ofInstant(Instant.ofEpochMilli(timestamp), ZoneId.systemDefault());
                }
                DateTime dateTime = DateUtil.parse(source);
                return DateUtil.toLocalDateTime(dateTime);
            }
        };
        Converter<String, LocalDate> localDateConverter = new Converter<String, LocalDate>() {
            @Override
            public LocalDate convert(String source) {
                if (!StringUtils.hasText(source)) {
                    return null;
                }
                if (NumberUtil.isNumber(source)) {
                    long timestamp = Long.parseLong(source);
                    return LocalDateTime.ofInstant(Instant.ofEpochMilli(timestamp), ZoneId.systemDefault()).toLocalDate();
                }
                DateTime dateTime = DateUtil.parse(source);
                return DateUtil.toLocalDateTime(dateTime).toLocalDate();
            }
        };
        registry.addConverter(dateConverter);
        registry.addConverter(localDateTimeConverter);
        registry.addConverter(localDateConverter);
    }

    /**
     * json日期参数序列化
     */
    @Bean
    public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
        // json日期参数序列化器，序列化成时间戳
        JsonSerializer<LocalDateTime> localDateTimeSerializer = new JsonSerializer<LocalDateTime>() {
            @Override
            public void serialize(LocalDateTime localDateTime, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
                if (localDateTime != null) {
                    long timestamp = localDateTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
                    jsonGenerator.writeNumber(timestamp);
                }
            }
        };
        JsonSerializer<LocalDate> localDateSerializer = new JsonSerializer<LocalDate>() {
            @Override
            public void serialize(LocalDate localDate, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
                if (localDate != null) {
                    long timestamp = localDate.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
                    jsonGenerator.writeNumber(timestamp);
                }
            }
        };
        // json日期参数反序列化器，支持时间戳和yyyy-MM-dd HH:mm:ss
        JsonDeserializer<LocalDateTime> localDateTimeDeserializer = new JsonDeserializer<LocalDateTime>() {
            @Override
            public LocalDateTime deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
                String value = jsonParser.getValueAsString();
                if (NumberUtil.isNumber(value)) {
                    long timestamp = Long.parseLong(value);
                    return LocalDateTime.ofInstant(Instant.ofEpochMilli(timestamp), ZoneId.systemDefault());
                } else if (StrUtil.isNotBlank(value)) {
                    DateTime dateTime = DateUtil.parse(value);
                    return DateUtil.toLocalDateTime(dateTime);
                } else {
                    return null;
                }
            }
        };
        JsonDeserializer<LocalDate> localDateDeserializer = new JsonDeserializer<LocalDate>() {
            @Override
            public LocalDate deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
                String value = jsonParser.getValueAsString();
                if (NumberUtil.isNumber(value)) {
                    long timestamp = Long.parseLong(value);
                    return LocalDateTime.ofInstant(Instant.ofEpochMilli(timestamp), ZoneId.systemDefault()).toLocalDate();
                } else if (StrUtil.isNotBlank(value)) {
                    DateTime dateTime = DateUtil.parse(value);
                    return DateUtil.toLocalDateTime(dateTime).toLocalDate();
                } else {
                    return null;
                }
            }
        };
        return builder -> builder
                .serializerByType(LocalDateTime.class, localDateTimeSerializer).deserializerByType(LocalDateTime.class, localDateTimeDeserializer)
                .serializerByType(LocalDate.class, localDateSerializer).deserializerByType(LocalDate.class, localDateDeserializer);
    }
}
